package com.whtq.front.common.util;

import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * Created by
 *
 * @author wmi
 * @date 2020/6/18 - 11:33.
 * @description
 */
public class RSAUtil {

    public static final String  SIGN_ALGORITHMS = "SHA1WithRSA";

    /**加密算法RSA*/
    public static final String KEY_ALGORITHM = "RSA";

    /**RSA最大加密明文大小*/
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**RSA最大解密密文大小*/
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * RSA签名
     * @param content 待签名数据
     * @param privateKey 商户私钥
     * @param input_charset 编码格式
     */
    public static String sign(String content, String privateKey, String input_charset){
        try {
            PKCS8EncodedKeySpec priPKCS8 	= new PKCS8EncodedKeySpec(Base64_.decode(privateKey) );
            KeyFactory keyf 				= KeyFactory.getInstance(KEY_ALGORITHM);
            PrivateKey priKey 				= keyf.generatePrivate(priPKCS8);
            java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
            signature.initSign(priKey);
            signature.update(content.getBytes(input_charset));
            byte[] signed = signature.sign();
            return Base64_.encode(signed);
        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * RSA验签名检查
     * @param content 待签名数据
     * @param sign 签名值
     * @param publicKey 支付宝公钥
     * @param input_charset 编码格式
     */
    public static boolean verify(String content, String sign, String publicKey, String input_charset){
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            byte[] encodedKey = Base64_.decode(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
            signature.initVerify(pubKey);
            signature.update(content.getBytes(input_charset) );
            boolean bverify = signature.verify( Base64_.decode(sign) );
            return bverify;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 私钥解密
     * @param content 密文
     * @param private_key 商户私钥
     * @param input_charset 编码格式
     */
    public static String decrypt(String content, String private_key, String input_charset) throws Exception {
        PrivateKey prikey = getPrivateKey(private_key);
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, prikey);
        InputStream ins = new ByteArrayInputStream(Base64_.decode(content));
        ByteArrayOutputStream writer = new ByteArrayOutputStream();
        //rsa解密的字节大小最多是128，将需要解密的内容，按128位拆开解密
        byte[] buf = new byte[128];
        int bufl;
        while ((bufl = ins.read(buf)) != -1) {
            byte[] block = null;
            if (buf.length == bufl) {
                block = buf;
            } else {
                block = new byte[bufl];
                for (int i = 0; i < bufl; i++) {
                    block[i] = buf[i];
                }
            }
            writer.write(cipher.doFinal(block));
        }
        return new String(writer.toByteArray(), input_charset);
    }

    /**
     * 公钥加密
     * @param data 源数据
     * @param publicKey 公钥(BASE64编码)
     */
    public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
        byte[] keyBytes = Base64_.decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }

    public static String encryptByPublicKeyToStr(byte[] data, String publicKey) throws Exception {
        byte[] bytes = encryptByPublicKey(data, publicKey);
        String result = Base64_.encode(bytes);
        return result;
    }

    /**
     * 得到私钥
     * @param key 密钥字符串（经过base64编码）
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String key) throws Exception {
        byte[] keyBytes;
        keyBytes = Base64_.decode(key);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    public static class Base64_ {
        private static final Base64.Decoder decoder = Base64.getDecoder();
        private static final Base64.Encoder encoder = Base64.getEncoder();
        public static byte[] decode(String key) {
            if (key == null) { return null;}
            byte[] decode = decoder.decode(key);
            return decode;
        }
        public static String encode(byte[] content) {
            if (content == null) {return null;}
            String encode = encoder.encodeToString(content);
            return encode;
        }
    }

    /**
     * 测试
     */
    public static void main(String args[]) throws Exception{
        /*
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        keyPairGen.initialize(1024);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        String pubKey = Base64_.encode(publicKey.getEncoded()); //公钥
        String priKey = Base64_.encode(privateKey.getEncoded()); //私钥
        */

        String pubKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCk5ZueKHAWBzyzE6jP/TB4ry+c\n" +
                "Ju9nyJ4jiElLDAXPogAC5RQFdGMyXXfbdHRmRafwWHSNUI4zzb09gidbU/06gVx4\n" +
                "cOiBIK5Gp1V+jeJ6NyMhqLC0VsTV3vtwxVGuyb7xR08BQ+vIt2EHgzUGeczuo58p\n" +
                "mEZKzV34pdwqw0j/4wIDAQAB"; //公钥
        String priKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKTlm54ocBYHPLMT\n" +
                "qM/9MHivL5wm72fIniOISUsMBc+iAALlFAV0YzJdd9t0dGZFp/BYdI1QjjPNvT2C\n" +
                "J1tT/TqBXHhw6IEgrkanVX6N4no3IyGosLRWxNXe+3DFUa7JvvFHTwFD68i3YQeD\n" +
                "NQZ5zO6jnymYRkrNXfil3CrDSP/jAgMBAAECgYACaRDTSQgKamSqwXjQQYU53mW1\n" +
                "xKPvQnF9V3qqzCwxks6pPCwEwGTK0a702EjzTcVcsA5UGn9ZpoJDqellXl7vO/6f\n" +
                "IiejKrMnEY1hNkKe9wSk06bB5b3zeNJXgz+0zMaV5Ga3k+4STU/j2MR1xYKXI6ql\n" +
                "YAgQPU03T58jB9dLEQJBANLG2NF+GQGZvvE855osBjjceDjeNLs3k/qbxGI2fbAh\n" +
                "YUPWB5kEb7UqRrn71yaoTptbU1q+I+53j4B+XwaeiHUCQQDIRsInflzO0+yIjFxC\n" +
                "Ui8lZSqxq5q7H+fmBt2qZXeEq0eZfUGlHlVup78ByvHfEkfHp4ADbVtqiUDWrtuC\n" +
                "Uxv3AkBxMuPtFtjdODIJI1mLPkuVLCrQ993AEmhB9ngw+uzjs/ml0gPAlVZdKLhV\n" +
                "WEDDfS7CVvjcSjPH9aQ3MSddybSlAkAfdfYT7Ca6GtJiVOI9TNm5Hx5I0LM6x/VO\n" +
                "zi9OEChpP9q8gs+oF47GFjXMG0xJhX6fH3Hdl/UvVCZKUyeKXnHbAkEAj8Ji1TnX\n" +
                "43UEfDMU1qXt89c6LWAPk636MN7MxLfjpgfYxjJYG2WWU4h6KxD/FLu8We09JEkJ\n" +
                "BAZgZlITr/O3PQ=="; //私钥

        priKey = priKey.replace("\n", "");
        pubKey = pubKey.replace("\n", "");

        //......................................公钥加密，私钥解密！

        String sourceStr = "id=123456&name=张三&age=23";
        //公钥加密
        byte[] ens = RSAUtil.encryptByPublicKey(sourceStr.getBytes(),pubKey);
        String encodes = Base64_.encode(ens);
        System.out.println(encodes);
        //私钥解密
        String res2 = RSAUtil.decrypt(encodes, priKey, "UTF-8");
        System.out.println(res2);

        //.......................................签名验证，私钥签名，公钥验证是否正确!

        String source = "id=123456&name=张三&age=23"; //模拟请求参数,然后对此请求参数进行签名。

        String sign = RSAUtil.sign(source, priKey, "UTF-8");

        System.out.println("签名是："+sign);

        // 公钥验证签名是否正确.
        // 具体流程是，请求参数+sign发送给公钥方，然后公钥方过滤掉sign，用公钥验证其他参数是否通过。

        boolean result = RSAUtil.verify(source, sign, pubKey, "UTF-8");

        System.out.println(result);

    }
}